babl: add support for grayscale spaces
authorØyvind Kolås <pippin@gimp.org>
Sun, 18 Aug 2019 22:01:13 +0000 (00:01 +0200)
committerØyvind Kolås <pippin@gimp.org>
Sun, 18 Aug 2019 22:02:43 +0000 (00:02 +0200)
A grayscale space is just like an RGB space, but has a default set
of chromaticities for R,G,B. When ICC profiles are loaded only the TRC
is considered is considered (we could also include the whitepoint),
blackpoint tag is ignored (and should be baked into the used ICC
profile instead.)

This works with GIMP-2.10 but master of GIMP currently hangs when
trying to load a grayscale jpeg with attached grayscale ICC profile,
The if #0 on line 999 of babl/babl-icc.c needs to be turned into a
1 to enable grayscale icc profiles for further testing.

babl/babl-icc.c
babl/babl-space.c
babl/babl-space.h
babl/babl.h
export-symbols

index e37197718c172ce381328709f51793c9e9a8d88d..9808c75c58d7f3b2517685093d1cd1d96e7c6e34 100644 (file)
@@ -561,12 +561,13 @@ switch (trc->type)
 static void 
 symmetry_test (ICC *state);
 
-char *
-babl_space_to_icc (const Babl  *babl,
-                         const char  *description,
-                         const char  *copyright,
-                         BablICCFlags flags,
-                         int         *ret_length)
+
+static char *
+babl_space_to_icc_rgb (const Babl  *babl,
+                       const char  *description,
+                       const char  *copyright,
+                       BablICCFlags flags,
+                       int         *ret_length)
 {
   const BablSpace *space = &babl->space;
   char icc[65536];
@@ -691,6 +692,119 @@ babl_space_to_icc (const Babl  *babl,
   }
 }
 
+
+static char *
+babl_space_to_icc_gray (const Babl  *babl,
+                        const char  *description,
+                        const char  *copyright,
+                        BablICCFlags flags,
+                        int         *ret_length)
+{
+  const BablSpace *space = &babl->space;
+  char icc[65536];
+  int length=65535;
+  ICC *state = icc_state_new (icc, length, 10);
+
+  icc[length]=0;
+
+  symmetry_test (state);
+
+  icc_write (sign, 4, "babl");  // ICC verison
+  icc_write (u8, 8, 2);         // ICC verison
+  icc_write (u8, 9, 0x20);      // 2.2 for now..
+  icc_write (u32,64, 0);        // rendering intent
+
+  icc_write (s15f16,68, 0.96421); // Illuminant
+  icc_write (s15f16,72, 1.0);
+  icc_write (s15f16,76, 0.82491);
+
+  icc_write (sign, 80, "babl"); // creator
+
+  icc_write (sign, 12, "mntr");
+  icc_write (sign, 16, "GRAY");
+  icc_write (sign, 20, "XYZ ");
+
+  icc_write (u16, 24, 2222);  // babl profiles
+  icc_write (u16, 26, 11);    // should
+  icc_write (u16, 28, 11);    // use a fixed
+  icc_write (u16, 30,  3);    // date
+  icc_write (u16, 32, 44);    // that gets updated
+  icc_write (u16, 34, 55);    // when the generator changes
+
+  icc_write (sign, 36, "acsp"); // changes
+
+  {
+    state->tags = 6; /* note: we could reserve a couple of spots and
+                        still use a very simple allocator and
+                        still be valid - albeit with tiny waste of
+                        space.
+                */
+    state->no = state->o = 128 + 4 + 12 * state->tags;
+
+    icc_write (u32,  128, state->tags);
+
+    icc_allocate_tag (state, "wtpt", 20);
+    icc_write (sign, state->o, "XYZ ");
+    icc_write (u32,  state->o + 4, 0);
+    icc_write (s15f16, state->o + 8,  space->whitepoint[0]);
+    icc_write (s15f16, state->o + 12, space->whitepoint[1]);
+    icc_write (s15f16, state->o + 16, space->whitepoint[2]);
+
+    write_trc (state, "kTRC", &space->trc[0]->trc, flags);
+
+    {
+      char str[128]="CC0/public domain";
+      int i;
+      if (!copyright) copyright = str;
+      icc_allocate_tag(state, "cprt", 8 + strlen (copyright) + 1);
+      icc_write (sign, state->o, "text");
+      icc_write (u32, state->o + 4, 0);
+      for (i = 0; copyright[i]; i++)
+        icc_write (u8, state->o + 8 + i, copyright[i]);
+    }
+    {
+      char str[128]="babl";
+      int i;
+      if (!description) description = str;
+      icc_allocate_tag(state, "desc", 90 + strlen (description) + 0);
+      icc_write (sign, state->o,"desc");
+      icc_write (u32, state->o + 4, 0);
+      icc_write (u32, state->o + 8, strlen(description) + 1);
+      for (i = 0; description[i]; i++)
+        icc_write (u8, state->o + 12 + i, description[i]);
+    }
+    icc_write (u32, 0, state->no + 0);
+    length = state->no + 0;
+  }
+
+  if (ret_length)
+    *ret_length = length;
+
+  babl_free (state);
+  {
+    char *ret = malloc (length);
+    memcpy (ret, icc, length);
+    return ret;
+  }
+}
+
+char *
+babl_space_to_icc (const Babl  *babl,
+                   const char  *description,
+                   const char  *copyright,
+                   BablICCFlags flags,
+                   int         *ret_length)
+{
+  if (babl->space.icc_type == BablICCTypeRGB)
+    return babl_space_to_icc_rgb (babl, description, copyright, flags,
+                                  ret_length);
+  else if (babl->space.icc_type == BablICCTypeGray)
+    return babl_space_to_icc_gray (babl, description, copyright, flags,
+                                   ret_length);
+  fprintf (stderr, "unexpected icc type in %s\n", __FUNCTION__);
+  return NULL;
+}
+
 const char *
 babl_space_get_icc (const Babl *babl, 
                     int        *length)
@@ -819,9 +933,11 @@ babl_space_from_icc (const char   *icc_data,
   const Babl *trc_red   = NULL;
   const Babl *trc_green = NULL;
   const Babl *trc_blue  = NULL;
+  const Babl *trc_gray  = NULL;
   const char *int_err;
   Babl *ret = NULL;
   int speed_over_accuracy = intent & BABL_ICC_INTENT_PERFORMANCE;
+  int is_gray = 0;
 
   sign_t profile_class, color_space, pcs;
 
@@ -842,7 +958,6 @@ babl_space_from_icc (const char   *icc_data,
        ret = _babl_space_for_lcms (icc_data, icc_length);
        if (ret->space.icc_type == BablICCTypeCMYK)
          return ret;
-       ret->space.icc_type = BablICCTypeCMYK;
        ret->space.icc_length = icc_length;
        ret->space.icc_profile = malloc (icc_length);
        memcpy (ret->space.icc_profile, icc_data, icc_length);
@@ -877,12 +992,23 @@ babl_space_from_icc (const char   *icc_data,
        return ret;
     }
 
-    if (strcmp (color_space.str, "RGB "))
-      *error = "not defining an RGB space";
+
+
+
+    if (strcmp (color_space.str, "RGB ")
+#if 0  /* XXX: commented out, as gimp-2.99 doesn't like loading grayscale jpegs with grayscale icc profiles when it is enabled */
+        && strcmp (color_space.str, "GRAY")
+#endif
+    )
+    {
+      *error = "not defining RGB, CMYK or GRAY space..";
+    }
     else
      {
        if (strcmp (profile_class.str, "mntr"))
          *error = "not a monitor-class profile";
+       if (!strcmp (color_space.str, "GRAY"))
+         is_gray = 1;
      }
   }
 
@@ -946,11 +1072,25 @@ babl_space_from_icc (const char   *icc_data,
      {
        trc_blue = babl_trc_from_icc (state, offset, error);
      }
+     if (!*error && icc_tag (state, "kTRC", &offset, &element_size))
+     {
+       trc_gray = babl_trc_from_icc (state, offset, error);
+     }
   }
 
-  if (!*error && (!trc_red || !trc_green || !trc_blue))
+  if (is_gray)
   {
-     *error = "missing TRCs";
+     if (!*error && (!trc_gray))
+     {
+        *error = "missing TRC";
+     }
+  }
+  else
+  {
+     if (!*error && (!trc_red || !trc_green || !trc_blue))
+     {
+        *error = "missing TRCs";
+     }
   }
 
   if (*error)
@@ -960,6 +1100,27 @@ babl_space_from_icc (const char   *icc_data,
     return NULL;
   }
 
+  if (is_gray)
+  {
+    int offset, element_size;
+    if (icc_tag (state, "wtpt", &offset, &element_size))
+    {
+    //   wX = icc_read (s15f16, offset + 8);
+    //   wY = icc_read (s15f16, offset + 8 + 4);
+    //   wZ = icc_read (s15f16, offset + 8 + 4 * 2);
+    }
+    ret  = (void*)babl_space_from_gray_trc (NULL, trc_gray, 1);
+    ret->space.icc_length = icc_length;
+    ret->space.icc_profile = malloc (icc_length);
+    memcpy (ret->space.icc_profile, icc_data, icc_length);
+    babl_free (state);
+    return ret;
+
+
+    *error = "gray parsing NYI";
+  }
+  else
+  {
   if (icc_tag (state, "rXYZ", NULL, NULL) &&
       icc_tag (state, "gXYZ", NULL, NULL) &&
       icc_tag (state, "bXYZ", NULL, NULL) &&
@@ -1076,8 +1237,9 @@ babl_space_from_icc (const char   *icc_data,
        return ret;
      }
   }
-
   *error = "didnt find RGB primaries";
+  }
+
   babl_free (state);
   return NULL;
 }
index 7a8d7b1f70d7e2a827155314834ed431f6bf21df..c05796ca88bb70cc015f5b68a01eb347e51b9d24 100644 (file)
@@ -243,6 +243,7 @@ _babl_space_for_lcms (const char *icc_data,
   memset (&space, 0, sizeof(space));
   space.instance.class_type = BABL_SPACE;
   space.instance.id         = 0;
+  space.icc_type = BablICCTypeCMYK;
 
   if (i >= MAX_SPACES-1)
   {
@@ -292,12 +293,14 @@ babl_space_from_rgbxyz_matrix (const char *name,
   space.RGBtoXYZ[6] = rz;
   space.RGBtoXYZ[7] = gz;
   space.RGBtoXYZ[8] = bz;
+  space.icc_type = BablICCTypeRGB;
 
   babl_matrix_invert (space.RGBtoXYZ, space.XYZtoRGB);
 
   babl_matrix_to_float (space.RGBtoXYZ, space.RGBtoXYZf);
   babl_matrix_to_float (space.XYZtoRGB, space.XYZtoRGBf);
 
+  /* recover chromaticities from matrix */
   {
     double red[3]={1.,.0,.0};
     double xyz[3]={1.,.0,.0};
@@ -392,6 +395,7 @@ babl_space_from_chromaticities (const char *name,
   space.whitepoint[0] = wx / wy;
   space.whitepoint[1] = 1.0;
   space.whitepoint[2] = (1.0 - wx - wy) / wy;
+  space.icc_type = BablICCTypeRGB;
 
   for (i = 0; space_db[i].instance.class_type; i++)
   {
@@ -426,6 +430,67 @@ babl_space_from_chromaticities (const char *name,
   return (Babl*)&space_db[i];
 }
 
+const Babl *
+babl_space_from_gray_trc (const char *name,
+                          const Babl *trc_gray,
+                          BablSpaceFlags flags)
+{
+  int i=0;
+  BablSpace space = {0,};
+  space.instance.class_type = BABL_SPACE;
+  space.instance.id         = 0;
+
+  space.xw = 0.3127;
+  space.yw = 0.3290;
+
+  space.xr = 0.639998686;
+  space.yr = 0.330010138;
+  space.xg = 0.300003784;
+  space.yg = 0.600003357;
+  space.xb = 0.150002046;
+  space.yb = 0.059997204;
+  space.trc[0] = trc_gray;
+  space.trc[1] = trc_gray;
+  space.trc[2] = trc_gray;
+
+  space.whitepoint[0] = space.xw / space.yw;
+  space.whitepoint[1] = 1.0;
+  space.whitepoint[2] = (1.0 - space.xw - space.yw) / space.yw;
+  space.icc_type = BablICCTypeGray;
+
+  for (i = 0; space_db[i].instance.class_type; i++)
+  {
+    int offset = ((char*)&space_db[i].xr) - (char*)(&space_db[i]);
+    int size   = ((char*)&space_db[i].trc) + sizeof(space_db[i].trc) - ((char*)&space_db[i].xr);
+
+    if (memcmp ((char*)(&space_db[i]) + offset, ((char*)&space) + offset, size)==0)
+      {
+        return (void*)&space_db[i];
+      }
+  }
+  if (i >= MAX_SPACES-1)
+  {
+    babl_log ("too many BablSpaces");
+    return NULL;
+  }
+  space_db[i]=space;
+  space_db[i].instance.name = space_db[i].name;
+  if (name)
+    snprintf (space_db[i].name, sizeof (space_db[i].name), "%s", name);
+  else
+          /* XXX: this can get longer than 256bytes ! */
+    snprintf (space_db[i].name, sizeof (space_db[i].name),
+             "space-gray-%s", babl_get_name(space.trc[0]));
+
+  /* compute matrixes */
+  babl_space_compute_matrices (&space_db[i], 1);
+
+  //babl_space_get_icc ((Babl*)&space_db[i], NULL);
+  return (Babl*)&space_db[i];
+
+}
+
+
 void
 babl_space_class_for_each (BablEachFunction each_fun,
                            void            *user_data)
@@ -1309,6 +1374,13 @@ babl_space_is_cmyk (const Babl *space)
   return space?space->space.icc_type == BablICCTypeCMYK:0;
 }
 
+int
+babl_space_is_gray (const Babl *space)
+{
+  return space?space->space.icc_type == BablICCTypeGray:0;
+}
+
+
 /* Trademarks:
  *
  * International Color Consortium is a registered trademarks of the.
index 1d9934a202c0c197755f3cfb13d485312d7ebd3a..86692e95b9ea2fcd56ef7df700b7829f1feeffdd 100644 (file)
@@ -111,10 +111,14 @@ typedef struct
   double           xb;  // blue primary chromaticity
   double           yb;
 
+  BablICCType icc_type;  /* taken into account when looking for duplicate spaces*/
+  double whitepoint[3]; /* CIE XYZ whitepoint */
   const Babl      *trc[3];
+
+  /* ------------- end of dedup zone --------------  */
+
   char             name[512]; // XXX: allocate this dynamically instead -
                               //      or use iccv4 style hashes for name.
-  double whitepoint[3]; /* CIE XYZ whitepoint */
 
   double RGBtoXYZ[9]; /* matrices for conversions */
   double XYZtoRGB[9];
@@ -131,7 +135,6 @@ typedef struct
    */
   char *icc_profile;
   int   icc_length;
-  BablICCType icc_type;
   BablCMYK cmyk;
 } BablSpace;
 
@@ -163,6 +166,10 @@ static inline void _babl_space_from_xyz (const Babl *space, const double *xyz, d
 void
 babl_space_class_init (void);
 
+const Babl *
+babl_space_from_gray_trc (const char *name,
+                          const Babl *trc_gray,
+                          BablSpaceFlags flags);
 
 
 #endif
index 25e5aec76dd574e1d60d2120e699250c0a59dc25..3c025bb808b4eb63fe4a33456a9c0bc76dff4891 100644 (file)
@@ -704,6 +704,7 @@ babl_space_from_rgbxyz_matrix (const char *name,
 const char * babl_format_get_encoding (const Babl *babl);
 
 int babl_space_is_cmyk (const Babl *space);
+int babl_space_is_gray (const Babl *space);
 
 /* values below this are stored associated with this value, it should also be
  * used as a generic alpha zero epsilon in GEGL to keep the threshold effects
index d692f46d78a081eafbdd3151298687fe28151e85..9123e7e3640a91f337e766a064de76d4c2d5219f 100644 (file)
@@ -55,6 +55,7 @@ babl_space_from_xyz
 babl_space_to_icc
 babl_space_with_trc
 babl_space_is_cmyk
+babl_space_is_gray
 babl_icc_make_space
 babl_icc_get_key
 babl_ticks